package com.kclm.xsap.web.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kclm.xsap.entity.*;
import com.kclm.xsap.service.*;
import com.kclm.xsap.utils.R;
import com.kclm.xsap.utils.captcha.CaptchaUtil;
import com.kclm.xsap.utils.exception.RRException;
import com.kclm.xsap.utils.random.RandomCodeGenerator;
import com.kclm.xsap.vo.ModifyPassword;
import com.kclm.xsap.vo.TeacherClassRecordVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;


/**
 * 员工管理控制器
 */
@Slf4j
@Controller
@RequestMapping("/user")
public class EmployeeController {

    private static final String UPLOAD_IMAGES_TEACHER_IMG = "upload/images/teacher_img/";

    private static String IGNORE = "version";

    @Resource
    private EmployeeService employeeService;

    @Resource
    private ScheduleRecordService scheduleRecordService;

    @Resource
    private CourseService courseService;

    @Resource
    private ClassRecordService classRecordService;

    @Resource
    private MemberService memberService;

    @Resource
    private ReservationRecordService reservationRecordService;


    /**
     * 随意验证码，暂时用不到了
     */
    @GetMapping("/getCode")
    @ResponseBody
    public R getCode(HttpSession session) {
        //从session获取之前生成的验证码
        String sessionOldCode = session.getAttribute("code").toString();
        log.debug("\n==>sessionOldCode==>{}", sessionOldCode);
        //生成新的验证码
        String newCode = RandomCodeGenerator.generateRandomCode(4);
        log.debug("\n==>随机生成的新验证码code==>{}", newCode);
        //将新生成的验证码放入session
        session.setAttribute("code", newCode);

        return R.ok().put("code", newCode);
    }


    /**
     * 前往登录页面
     */
    /*@GetMapping({"/toLogin"})
    public String toLogin(HttpSession session, Model model) {
        String code = RandomCodeGenerator.generateRandomCode(4);
        log.debug("\n==>随机生成的验证码code==>{}", code);
        session.setAttribute("code", code);
        model.addAttribute("code", code);
        return "x_login";
    }*/

    /**
     * 跳转到登录页面
     */
    @GetMapping({"/toLogin"})
    public String toLogin(HttpSession session, Model model) throws IOException {
        //获取验证码图片
        byte[] captchaImageBytes = CaptchaUtil.generateCaptchaImage(session);
        //将验证码图片转为base64字符串
        String captchaImageBase64String = Base64.getEncoder().encodeToString(captchaImageBytes);
        //验证码文本
        String captchaText = session.getAttribute("code").toString();

        model.addAttribute("captchaImage", captchaImageBase64String);
        model.addAttribute("code", captchaText);

        return "x_login";
    }


    /**
     * 获取验证码图片和验证码文本
     */
    @GetMapping("/getCaptcha")
    @ResponseBody
    public R getCaptcha(HttpSession session) throws IOException {
        byte[] captchaImageBytes = CaptchaUtil.generateCaptchaImage(session);
        System.out.println(captchaImageBytes);
        String captchaText = session.getAttribute("code").toString();
        return R.ok().put("captchaImageBytes", captchaImageBytes).put("captchaText", captchaText);
    }


    /**
     * 登录表单
     */
    @PostMapping("/login")
    public String login(@RequestParam("username") String username,
                        @RequestParam("password") String password,
                        @RequestParam("code") String code,
                        HttpSession session,
                        RedirectAttributes attributes) {

        log.info("前端传入的验证码code====>{}" + code);
        //随机生成四位验证码
        String randomCode = session.getAttribute("code").toString();
        //验证码校验
        if (randomCode.equalsIgnoreCase(code)) {
            EmployeeEntity isExistEmp = employeeService.getOne((new QueryWrapper<EmployeeEntity>().eq("name", username).eq("role_password", password)));
            if (isExistEmp != null) {
                isExistEmp.setLastModifyTime(LocalDateTime.now());
                employeeService.updateById(isExistEmp);
                session.setAttribute("LOGIN_USER", isExistEmp);
                return "redirect:/index/index";
            } else {
                attributes.addFlashAttribute("USER_OR_PWD_NOT_EXIST", true);
                return "redirect:/user/toLogin";
            }
        } else {
            attributes.addFlashAttribute("CODE_ERROR", true);
            //跳转到这个请求方法toLogin
            return "redirect:/user/toLogin";
        }
    }

    /**
     * 修改信息
     */
    @PostMapping("/modifyUser.do")
    public String modifyUser(EmployeeEntity employeeEntity, Model model) {
        System.out.println(employeeEntity);
        EmployeeEntity employeeEntityById = employeeService.getById(employeeEntity);
        employeeEntityById.setName(employeeEntity.getName()).setPhone(employeeEntity.getPhone()).setSex(employeeEntity.getSex());
        employeeService.updateById(employeeEntityById);
        model.addAttribute("userInfo", employeeEntityById);
        return "x_profile";

    }

    /**
     * 跳转员工管理页面
     */
    @GetMapping("/x_teacher_list.do")
    public String togoTeacherList() {
        return "employee/x_teacher_list";
    }

    /**
     * 跳转添加老师页面
     */
    @GetMapping("/x_teacher_add.do")
    public String togoTeacherAdd() {
        return "employee/x_teacher_add";
    }

    /**
     * 前端点击编辑后跳转到更新页面
     */
    @GetMapping("/x_teacher_update.do")
    public ModelAndView togoTeacherUpdate(@RequestParam("id") Long id) {
        EmployeeEntity teacherById = employeeService.getById(id);
        int samePhoneCount = employeeService.count(new QueryWrapper<EmployeeEntity>().eq("phone", teacherById.getPhone()));

        ModelAndView mv = new ModelAndView();
        if (samePhoneCount > 1) {
            mv.addObject("CHECK_PHONE_EXIST", true);
        }
        mv.setViewName("employee/x_teacher_update");
        mv.addObject("teacherMsg", teacherById);
        mv.addObject("birthdayStr", teacherById.getBirthday());
        return mv;
    }

    /**
     * 跳转员工详情页
     */
    @GetMapping("/x_teacher_list_data.do")
    public String togoTeacherListData(@RequestParam("id") Long id, Model model) {
        model.addAttribute("ID", id);
        return "employee/x_teacher_list_data";
    }

    /**
     * 跳转到充值页面
     */
    @GetMapping("/toEnsureUser")
    public String toEnsureUser() {
        return "x_ensure_user";
    }

    /**
     * 跳转更新页面
     */
    @GetMapping("/x_teacher_update")
    public ModelAndView updateTeacher(@RequestParam("id") Long id) {
        EmployeeEntity teacherById = employeeService.getById(id);

        ModelAndView mv = new ModelAndView();
        mv.addObject("teacherMsg", teacherById);
        mv.setViewName("employee/x_teacher_update");

        return mv;
    }

    /**
     * 重置密码
     */
    @GetMapping("/toResetPwd")
    public String toResetPwd(String userPhoneOrEmail, Model model) {
        String emailRegex = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$";
        String phoneRegex = "^1[0-9]{10}$";

        boolean isAEmail = Pattern.compile(emailRegex).matcher(userPhoneOrEmail).matches();
        boolean isAPhone = Pattern.compile(phoneRegex).matcher(userPhoneOrEmail).matches();
        if (isAEmail || isAPhone) {
            List<EmployeeEntity> userOne = employeeService.list(new QueryWrapper<EmployeeEntity>()
                    .eq("role_email", userPhoneOrEmail)
                    .or()
                    .eq("phone", userPhoneOrEmail));


            Long userId = userOne.get(0).getId();


            log.info("userOne===id==>" + userOne.get(0).getId());

            if (userOne.isEmpty()) {
                model.addAttribute("CHECK_USER_ERROR", true);
                return "x_ensure_user";
            }
            model.addAttribute("userId", userId);
            return "send_mail_ok";
        } else {
            model.addAttribute("CHECK_INPUT_FORMAT", true);
            return "x_ensure_user";
        }


    }

    /**
     * 跳转修改密码页面
     */
    @GetMapping("/x_modify_password.do")
    public String modifyPassword() {
        return "x_modify_password";
    }


    /**
     * 修改密码操作
     */
    @PostMapping("/modifyPwd.do")
    public String modifyPwd(ModifyPassword entity, Model model) {
        log.debug("\n==>打印前台传入的修改密码表单数据==>{}", entity);

        if (null == entity) {
            throw new RRException("修改数据表单为空", 22404);
        }
        if (entity.getOldPwd().isEmpty()) {
            model.addAttribute("CHECK_PWD_ERROR", 0);
            log.debug("\n==>原密码为空");
            return "x_modify_password";
        } else {
            if (!entity.getNewPwd().equals(entity.getPwd2())) {
                model.addAttribute("CHECK_PWD_ERROR", 2);
                log.debug("\n==>新密码两次不一样");
                return "x_modify_password";
            }
            EmployeeEntity employeeEntity = employeeService.getById(entity.getId());
            if (!employeeEntity.getRolePassword().equals(entity.getOldPwd())) {
                model.addAttribute("CHECK_PWD_ERROR", 1);
                log.debug("\n==>原密码不正确");
                return "x_modify_password";
            } else {
                employeeEntity.setRolePassword(entity.getNewPwd());
                boolean isUpdatePwd = employeeService.updateById(employeeEntity);
                if (isUpdatePwd) {
                    log.debug("\n==>修改成功！");
                    return "redirect:/index/logout";
                } else {
                    throw new RRException("修改密码失败", 22405);
                }
            }
        }
    }


    /**
     * 跳转个人资料页面
     */
    @GetMapping("/x_profile.do")
    public String profile(Long id, Model model) {
        EmployeeEntity employeeServiceById = employeeService.getById(id);
        System.out.println("个人资料:" + employeeServiceById);
        model.addAttribute("userInfo", employeeServiceById);
        return "x_profile";
    }


    /**
     * 返回所有老师信息
     */
    @GetMapping("/toSearch.do")
    @ResponseBody
    public R toSearch() {
        List<EmployeeEntity> allEmployeeList = employeeService.list(null);
        return new R().put("value", allEmployeeList);
    }


    /**
     * 获取所有员工信息
     */
    @PostMapping("/teacherList.do")
    @ResponseBody
    public R teacherList() {
        List<EmployeeEntity> teachers = employeeService.list(null);
        return new R().put("data", teachers);
    }


    /**
     * 更新保存员工信息
     */
    @PostMapping("/teacherEdit.do")
    @ResponseBody
    public R teacherEdit(EmployeeEntity entity) {
        EmployeeEntity employeeEntity = employeeService.getById(entity);
        BeanUtils.copyProperties(entity, employeeEntity, IGNORE);
        boolean isUpdate = employeeService.updateById(employeeEntity);
        log.debug("\n==>更新老师信息的结果==>{}", isUpdate);
        if (isUpdate) {
            return R.ok("更新成功！");
        } else {
            return R.error("更新失败！！");
        }
    }


    /**
     * 返回详情信息
     */
    @PostMapping("/teacherDetail.do")
    @ResponseBody
    public R teacherDetail(@RequestParam("tid") Long id) {
        EmployeeEntity teacherInfo = employeeService.getById(id);
        log.debug("\n==>打印返回到前端的老师详情信息==>{}", teacherInfo);
        return R.ok().put("data", teacherInfo);
    }


    /**
     * 老师管理中的上课记录信息
     */
    @PostMapping("/teacherClassRecord.do")
    @ResponseBody
    public R teacherClassRecord(@RequestParam("tid") Long id) {

        List<ScheduleRecordEntity> scheduleForTeacher = scheduleRecordService.list(new QueryWrapper<ScheduleRecordEntity>().eq("teacher_id", id));
        List<TeacherClassRecordVo> teacherClassRecordVos = scheduleForTeacher.stream().map(entity -> {
            //获取当前排课记录的id
            Long scheduleId = entity.getId();
            //获取当前排课的课程id
            Long courseId = entity.getCourseId();
            //根据课程id获取当前课程信息
            CourseEntity courseById = courseService.getById(courseId);
            //从课程信息里获取课程名
            String courseName = courseById.getName();
            //获取上课日期+时间
            //LocalDateTime classTime = LocalDateTime.of(entity.getStartDate(), entity.getClassTime());
            LocalDateTime classTime = LocalDateTime.of(entity.getStartDate(), entity.getClassTime());
            //格式化日期+时间

            String formatTime = classTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
            log.info("formatTime: " + formatTime);
            //获取课程单位消耗次数
            Integer timesCost = courseById.getTimesCost();
            //在上课记录中查询所有上了当前课程的会员的id的list
            List<Long> memberIdList = classRecordService.list(new QueryWrapper<ClassRecordEntity>().select("member_id").eq("schedule_id", scheduleId).eq("check_status", 1)).stream().map(ClassRecordEntity::getMemberId).collect(Collectors.toList());
            //初始化创建一个用于存放会员名的list，当会员id为空时
            List<String> memberNames = new ArrayList<>();

            //只有当查到的会员id不为空时，才进行查询
            if (!memberIdList.isEmpty()) {
                memberNames = memberService.list(new QueryWrapper<MemberEntity>().select("name").in("id", memberIdList)).stream().map(MemberEntity::getName).collect(Collectors.toList());
            }
            //创建一个存放会员名的builder
            StringBuilder memberNameBuilder = new StringBuilder();
            if (!memberNames.isEmpty()) {
                memberNameBuilder.append("【");
                //将所有会员名添加进去，补上','
                memberNames.forEach(name -> memberNameBuilder.append(name).append(","));
                //将最后一个','删掉
                memberNameBuilder.deleteCharAt(memberNameBuilder.lastIndexOf(","));
                memberNameBuilder.append("】");
            } else {
                memberNameBuilder.append("  --");
            }

            //赋值vo并收集成list
            return new TeacherClassRecordVo().setCourseName(courseName).setClassTime(formatTime).setCardName(memberNameBuilder.toString()).setTimesCost(timesCost);
        }).collect(Collectors.toList());

        log.debug("\n==>打印返回到前台的老师上课记录信息是：==>{}", teacherClassRecordVos);

        return R.ok().put("data", teacherClassRecordVos);
    }

    @Value("${upload.path}")
    private String uploadDir;


    @PostMapping("/modifyUserImg.do")
    @ResponseBody
    public R modifyUserImg(@RequestParam("id") Integer id, @RequestParam("avatarFile") MultipartFile file, HttpSession session) {

        try {
            String newFileName = UUID.randomUUID().toString();
            //获取原来文件的扩展名
            String originalFilename = file.getOriginalFilename();
            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
            newFileName = newFileName + suffix;

            Path uploadPath = Paths.get(uploadDir);
            if (!Files.exists(uploadPath)) {
                Files.createDirectories(uploadPath);
            }
            Path filePath = uploadPath.resolve(newFileName);

            // 使用流的方式上传文件
            try (InputStream inputStream = file.getInputStream(); OutputStream outputStream = Files.newOutputStream(filePath)) {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
            }

            EmployeeEntity employeeEntity = employeeService.getById(id);
            employeeEntity.setAvatarUrl(newFileName);

            //得到seesion中的登录用户信息
            EmployeeEntity loginUser = (EmployeeEntity) session.getAttribute("LOGIN_USER");

            String avatarUrl = loginUser.getAvatarUrl();
            System.out.println("avatarUrl:====>" + avatarUrl);

            //删除原来的头像
            if (avatarUrl != null && !avatarUrl.isEmpty()) {
                Path oldFilePath = uploadPath.resolve(avatarUrl);
                try {
                    Files.delete(oldFilePath);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            loginUser.setAvatarUrl(newFileName);
            log.info("loginUser:=====>" + loginUser);
            //更新session中的登录用户信息
            session.setAttribute("LOGIN_USER", loginUser);
            if (employeeService.updateById(employeeEntity)) {
                return R.ok("上传头像成功！");
            } else {
                return R.error("上传图像失败！");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return R.error("上传图像失败！");
    }


    /**
     * 反射获取上传文件的路径
     * 这里的图片上传和学员的头像上传是一样的, 这里没有用封装,学员用了封装
     */
    @GetMapping("/public/{fileName:.+}")
    @ResponseBody
    public ResponseEntity<org.springframework.core.io.Resource> serveFile(@PathVariable String fileName) {
        org.springframework.core.io.Resource file = loadFileAsResource(fileName);
        return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"").contentType(MediaType.IMAGE_JPEG).body(file);
    }

    private org.springframework.core.io.Resource loadFileAsResource(String fileName) {
        try {
            Path filePath = Paths.get(uploadDir).resolve(fileName).normalize();
            org.springframework.core.io.Resource resource = new org.springframework.core.io.UrlResource(filePath.toUri());
            if (resource.exists()) {
                return resource;
            } else {
                throw new RuntimeException("File not found " + fileName);
            }
        } catch (MalformedURLException ex) {
            throw new RuntimeException("File not found " + fileName, ex);
        }
    }


    /**
     * 添加员工
     */
    @PostMapping("/teacherAdd.do")
    @ResponseBody
    public R teacherAdd(@Valid EmployeeEntity entity, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            HashMap<String, String> map = new HashMap<>();
            bindingResult.getFieldErrors().forEach(item -> {
                String message = item.getDefaultMessage();
                String field = item.getField();
                map.put(field, message);
            });
            return R.error(400, "非法参数").put("errorMap", map);
        }
        boolean isSave = employeeService.save(entity);
        if (isSave) {
            return R.ok("添加成功!");
        } else {
            return R.error("添加失败");
        }
    }


    /**
     * 删除老师
     * 1.删除成功。该老师已没有排课记录和计划）
     * 2.删除成功。该老师存在未完成的排课计划，但尚没有预约记录，
     * 2.1该老师没有已完成的排课记录，删除该老师信息后未完成的排课计划也会被删除
     * 2.2该老师存在已完成的排课记录，删除该老师信息后未完成的排课计划也会被删除，但已完成的排课记录会被保留
     * 3.删除失败。该老师存在未完成的排课记录且有预约，无法删除该老师
     * 4.删除成功。该老师没有未完成的排课计划，仅删除该老师信息后保留已有的排课记录
     */
    @PostMapping("/deleteOne.do")
    @ResponseBody
    @Transactional
    public R deleteOne(@RequestParam("id") Long id) {
        //检查该老师的排课计划表
        List<ScheduleRecordEntity> allScheduleForCurrentTeacher = scheduleRecordService.list(new QueryWrapper<ScheduleRecordEntity>().select("id", "order_nums", "start_date", "class_time").eq("teacher_id", id));
        //当该老师没有排课记录和计划时,直接删除老师信息
        if (allScheduleForCurrentTeacher.isEmpty()) {
            log.debug("\n==>该老师已没有排课记录和计划,删除成功！！");
            boolean isRemove = employeeService.removeById(id);
            if (isRemove) {
                return R.ok("该老师已没有排课记录和计划");
            } else {
                throw new RuntimeException("删除失败！");
            }
        } else {
            //该老师有排课记录
            //取出该老师所有还未完成的排课计划
            List<ScheduleRecordEntity> allScheduleAfterThisMoment = allScheduleForCurrentTeacher.stream().filter(schedule -> {
                //获取上课精确完整日期
                LocalDateTime classStartDateTime = LocalDateTime.of(schedule.getStartDate(), schedule.getClassTime());
                //过滤留下该老师所有还未完成的排课计划【即此时此刻之后的排课计划】
                return classStartDateTime.isAfter(LocalDateTime.now());
            }).collect(Collectors.toList());

            if (allScheduleAfterThisMoment.isEmpty()) {
                //该老师没有未完成的排课计划，删除该老师信息后保留已有的排课记录
                boolean isRemove = employeeService.removeById(id);
                if (isRemove) {
                    return R.ok("该老师存在排课记录但没有未完成的排课计划，仅删除该老师信息后保留已有的排课记录");
                } else {
                    throw new RuntimeException("删除失败！");
                }
            } else {
                //该老师存在未完成的排课记录
                //获取所有未完成的排课计划的预约人数的和
                int sum = allScheduleAfterThisMoment.stream().mapToInt(ScheduleRecordEntity::getOrderNums).sum();
                log.debug("\n==>打印未完成的排课计划的预约人数之和==>{}", sum);
                if (sum > 0) {
                    //未完成的计划中已有会员进行预约，无法删除
                    return R.error("该老师存在未完成的排课记录且有预约，无法删除该老师");
                } else {
                    //获取未开始的全部排课的id-list
                    List<Long> scheduleIds = allScheduleAfterThisMoment.stream().map(ScheduleRecordEntity::getId).collect(Collectors.toList());
                    if (allScheduleAfterThisMoment.size() == allScheduleForCurrentTeacher.size()) {
                        //该老师只有未完成的排课记录，删除该老师信息后未完成的排课计划也会被删除，且没有预约记录
                        //删除老师
                        boolean isRemoveTeacher = employeeService.removeById(id);
                        //删除预约后取消预约导致预约人数显示0 的预约记录
                        reservationRecordService.remove(new QueryWrapper<ReservationRecordEntity>().in("schedule_id", scheduleIds));
                        //最后删除该老师的全部排课记录【因为没有已完成的记录】
                        boolean isRemoveSchedule = scheduleRecordService.remove(new QueryWrapper<ScheduleRecordEntity>().eq("teacher_id", id));
                        if (isRemoveSchedule && isRemoveTeacher) {
                            return R.ok("该老师只有未完成的排课记录，删除该老师信息后未完成的排课计划也会被删除");
                        } else {
                            //抛出异常回滚
                            throw new RRException("删除老师信息或该老师排课记录失败", 22405);
                        }
                    } else {

                        //该老师有已完成和未完成的排课记录，但未完成的排课计划没有预约记录 ，删除该老师信息后未完成的排课计划也会被删除，但已完成的排课记录会被保留
                        //首先通过老师id删除该老师
                        boolean isRemoveTeacher = employeeService.removeById(id);
                        //再通过该老师的所有未开始的全部排课计划的排课id删除所有未开始的排课的预约记录【目的是删除有预约但取消了的记录，此时预约人数未0，不用担心删错预约记录；而先删预约记录为了避免外键冲突】
                        boolean isRemoveReserveOfCancel = reservationRecordService.remove(new QueryWrapper<ReservationRecordEntity>().in("schedule_id", scheduleIds));
                        if (isRemoveReserveOfCancel) {
                        }
                        //最后删除这些未完成的排课记录【此时已经没有了外键约束】
                        boolean isRemoveSchedule = scheduleRecordService.removeByIds(scheduleIds);

                        if (isRemoveSchedule && isRemoveTeacher) {
                            return R.ok("该老师存在未完成的排课计划，删除该老师信息后未完成的排课计划也会被删除，但已完成的排课记录会被保留");
                        } else {
                            //抛出异常 回滚
                            throw new RRException("删除老师信息或该老师排课记录失败", 22406);
                        }
                    }
                }
            }
        }
    }
}